/*
 * BBDJTable.java
 *
 * Created on May 21, 2007, 8:44:17 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package bbdSwing;

/*
 * Copyright 2007 microneering, Inc and James Gamber
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 * BBD/bbdSwing/BBDJTable.java
 */
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;

import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import bbd.BBDBroker;
import bbd.BBDDefaultField;
import bbd.BBDDefaultRow;
import bbd.BBDField;
import bbd.BBDRow;
import bbd.BBDRowArrayList;

/**
 * 
 * @param F
 *            Field Type used for this table.
 * @param R
 *            This table displays rows from resultset
 * @param L
 *            The rows are stored in a row array list.
 * @author james gamber
 */
@SuppressWarnings("serial")
public class BBDJTable<R extends BBDRow, L extends BBDRowArrayList<R>>
		extends JTable {

	private final BBDBroker<R, L> broker;
	private List<R> deletedRow = new BBDRowArrayList<R>();;
	private List<Boolean> dirtyRowList = new ArrayList<Boolean>();


	/**
	 * Create a table using the select method from the broker and the field
	 * names for headings from the meta data.
	 * 
	 * @param broker
	 */
	public BBDJTable(final BBDBroker<R, L> broker) {
		super();

		this.broker = broker;

		populate();

	}

	/**
	 * Use database to put rows into table.
	 * 
	 * What rows are used is determined by the broker select method you write.
	 * 
	 * @return rows put in table
	 */
	public int populate() {

		final L al = broker.select(null);

		setColumn(al.getTitle());

		final DefaultTableModel dtm = (DefaultTableModel) getModel();
		
		// remove any existing rows
		for (int r = dtm.getRowCount()-1; r > -1; r--)
			dtm.removeRow(r);

		for (final R r : al) {
			List<String> list = new ArrayList<String>();
			if (r instanceof BBDDefaultRow) {
				BBDDefaultRow<BBDField> defRow = (BBDDefaultRow<BBDField>) r;
				for (BBDField field : defRow) {
					list.add(field.toString().trim());
				}

				final Vector<String> v = new Vector<String>(list);

				dtm.addRow(v);
			}
		}
		
		for (int i = 0; i < al.size(); i++)
			dirtyRowList.add(Boolean.FALSE);

		clearDirty();

		return this.getRowCount();
	}

	private void clearDirty() {
		deletedRow.clear();
		Collections.fill(dirtyRowList, Boolean.FALSE);
		
	}

	/**
	 * Set the column header
	 * @param ct string array of column header titles
	 */
	private void setColumn(final String[] ct) {

		final DefaultTableColumnModel tcm = (DefaultTableColumnModel) getColumnModel();
		
		if (ct.length > tcm.getColumnCount()) {
			final DefaultTableModel dtm = new DefaultTableModel(0, ct.length);
			setModel(dtm);
		}


		int i = 0;
		for (final String s : ct) {
			final TableColumn tc = tcm.getColumn(i++);
			
			tc.setHeaderValue(s);
		}

		// disable typing in a cell
		this.setEnabled(false);

		// sort columns
		RowSorter<TableModel> sorter = new TableRowSorter<TableModel>(this
				.getModel());
		this.setRowSorter(sorter);

	}

	/*
	 * Track changes to the table, so they can be persisted later.
	 * 
	 * (non-Javadoc)
	 * @see javax.swing.JTable#tableChanged(javax.swing.event.TableModelEvent)
	 */
	@SuppressWarnings("unchecked")
	public void tableChanged(TableModelEvent e) {

		super.tableChanged(e);

		final int col = e.getColumn();
		final int row = e.getFirstRow();

		if (col == -1)
			return;

		dirtyRowList.set(row, Boolean.TRUE);

	}

	/**
	 * Add blank row onto the end of the table.
	 *
	 */
	public void addNewRow() {
		
		final DefaultTableModel dtm = (DefaultTableModel) getModel();
		Vector<String> newRow = new Vector<String>();
		for (int c = 0; c < dtm.getColumnCount(); c++)
			newRow.add("");
		
		dtm.addRow(newRow);
		dirtyRowList.add(Boolean.FALSE);
		
	}

	/**
	 * Remove selected rows.  Track these rows
	 * so the deletion can be persisted.
	 * 
	 * @return number of rows removed
	 */
	@SuppressWarnings("unchecked")
	public int removeSelected() {

		final int min = selectionModel.getMinSelectionIndex();
		final int max = selectionModel.getMaxSelectionIndex();
		final DefaultTableModel dtm = (DefaultTableModel) getModel();

		int removedRows = 0;
		for (int r = max; r >= min; r--) {
			if (selectionModel.isSelectedIndex(r)) {
				// add to deleterows
				R bbdRow = (R) new BBDDefaultRow<BBDField>();
				for (int c = 0; c < dtm.getColumnCount(); c++)
					bbdRow.add(new BBDDefaultField(dtm.getValueAt(r, c)));
				deletedRow.add(bbdRow);

				dtm.removeRow(r);
				dirtyRowList.remove(r);
				removedRows++;
			}
		}
		
		return removedRows;
	}

	/**
	 * Determine if table data is out of sync with the database.
	 * 
	 * @return true if there are table changes that have not been persisted.
	 */
	public boolean isDirty() {
		return dirtyRowList.contains(Boolean.TRUE) || deletedRow.size() > 0;
	}

	@SuppressWarnings("unchecked")
	public int persist() {

		//**************************************
		// change all the dirty rows
		L rowList = (L) new BBDRowArrayList<R>();
		for (int r = 0; r < getModel().getRowCount(); r++) {
			
			if (dirtyRowList.get(r)) {
				R newRow = (R) new BBDDefaultRow<BBDField>();
				for (int c = 0; c < getModel().getColumnCount(); c++) {
					newRow.add(new BBDDefaultField(getModel().getValueAt(r, c)));
				}
				rowList.add(newRow);
			}
			
		}
		int rows = broker.update(rowList);
		
		//************************************************
		// remove deleted rows
		rowList.clear();
		for (R r : deletedRow) {
			rowList.add(r);
		}
		
		rows += broker.delete(rowList);
		
		clearDirty();
		
		return rows;
	}

}
